package com.apes.framework.plugin.soe.core.context;

import com.apes.framework.api.SpringManager;
import com.apes.framework.plugin.soe.core.service.ExecutionService;
import com.apes.framework.util.CacheUtil;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Lock;

/**
 * 功能：soe上下文
 *
 * @author xul
 * @create 2018-09-22 14:16
 */
public class SoeContext {

    protected static ThreadLocal<Queue<Command>> threadLocal = new ThreadLocal<>();

    /**
     * 增加一个元索
     */
    public static void add(Command command) {
        ExecutionService executionService = SpringManager.getBean(ExecutionService.class);
        if (!executionService.check(command.getEntity())) return;
        boolean isNull = threadLocal.get() == null;
        Queue<Command> queue = getQueue(threadLocal);
        queue.add(command);
        if (isNull) {
            executionService.execute();
        }
    }

    /**
     * 返回队列头部的元素
     */
    public static Command peek() {
        Queue<Command> queue = getQueue(threadLocal);
        if (queue.isEmpty()) {
            return null;
        }
        return queue.peek();
    }

    /**
     * 移除并返问队列头部的元素
     **/
    public static Command poll() {
        Queue<Command> queue = getQueue(threadLocal);
        if (queue.isEmpty()) {
            return null;
        }
        return queue.poll();
    }

    protected static <T> Queue<T> getQueue(ThreadLocal<Queue<T>> threadLocal) {
        Queue<T> queue = threadLocal.get();
        if (queue == null) {
            queue = new LinkedList<>();
            threadLocal.set(queue);
        }
        return queue;
    }

    /**
     * 调用remove方法能有效的杜绝内存泄露问题
     */
    public static void remove() {
        threadLocal.remove();
    }

    abstract static class Runner {

        public <T> T exec() {
            Lock lock = getLock();
            lock.lock();// 得到锁
            try {
                return doExec();
            } finally {
                lock.unlock();// 释放锁
            }
        }

        private Lock getLock() {
            String key = "soe_" + Thread.currentThread().getId();
            return CacheUtil.getLock(key);
        }

        abstract <T> T doExec();
    }
}